#include <iostream>
#include <iomanip>

#include "TFile.h"
#include "TTree.h"
#include "TH1F.h"
#include "TH2F.h"


#include "BuiltEvent.hh"
#include "CalEvent.hh"
#include "Settings.hh"
#include "Calibration.hh"
#include "Mux.hh"

using namespace std;

ClassImp(BuiltEvent);
ClassImp(CalEvent);
ClassImp(DgfData);
ClassImp(Mux);

int main(int argc, char* argv[]){
  char* InputFile = NULL;
  char* OutputFile = NULL;
  char* SettingFile = NULL;
  char* HistoFile = NULL;
  char* CalibrationFile = NULL;
  int VerboseLevel = 0;

  CommandLineInterface* interface = new CommandLineInterface();

  interface->Add("-i", "inputfile", &InputFile);
  interface->Add("-o", "outputfile", &OutputFile);
  interface->Add("-h", "histo file (optional)", &HistoFile);
  interface->Add("-s", "settings file", &SettingFile);
  interface->Add("-c", "calibration file", &CalibrationFile);
  interface->Add("-v", "verbose level", &VerboseLevel);
  interface->CheckFlags(argc, argv);

  if(InputFile == NULL || OutputFile == NULL){
    //if(InputFile == NULL || OutputFile == NULL ){
    cerr<<"You have to provide input, output and settings file!"<<endl;
    exit(1);
  }
  if(CalibrationFile == NULL){
    //if(InputFile == NULL || OutputFile == NULL ){
    cout<<"No Calibration File given, using default: nocal.dat"<<endl;
    CalibrationFile = "nocal.dat";
  }
  
  cout<<"input file: "<<InputFile<< endl;

  Settings* sett;
  sett = new Settings(SettingFile);
  Calibration *cal = new Calibration(CalibrationFile,sett);
  Mux *mux = new Mux(CalibrationFile,sett);
  CalEvent *calevent = new CalEvent(cal, sett); 


  TFile* infile = new TFile(InputFile);
  TTree* intree = (TTree*) infile->Get("tr");

  if(intree == NULL){
    cout << "could not find tree tr in file " << infile->GetName() << endl;
    return 3;
  }
  BuiltEvent* event = new BuiltEvent;

  intree->SetBranchAddress("Event",&event);
  TFile* outfile = new TFile(OutputFile,"recreate");
  if(outfile->IsZombie()){
    return 4;
  }
  TTree* outtree = new TTree("caltr","calibrated events");

  TFile* histofile;
  TH1F* EGamS[8][3][6];
  TH1F* EGamC[8][3];
  
  if(HistoFile != NULL){
    histofile = new TFile(HistoFile,"recreate");
    for(int i=0;i<8;i++){ // cluster
      for(int k=0;k<3;k++){ // crystal
	for(int j=0;j<6;j++){ // segment
	  EGamS[i][k][j] = new TH1F(Form("EGamCl%dCr%dSe%d",i,k,j),Form("EGamCl%dCr%dSe%d",i,k,j),65536, 0, 65536);
	}
	EGamC[i][k] = new TH1F(Form("EGamCl%dCr%d",i,k),Form("EGamCl%dCr%d",i,k),65536, 0, 65536);	
      }
    }
  }

  outfile->cd();


  vector<Barrel> *FBarrelBranch = calevent->GetFBarrel();
  vector<Barrel> *BBarrelBranch = calevent->GetBBarrel();
  vector<Annular> *FCDBranch = calevent->GetBCD();
  vector<Germanium> *MiniballBranch = calevent->GetCluster();
  long long EbisTime;
  long long T1Time;
  long long SuperCycleTime;

  int BufferSize = 1024000;

  outtree->Branch("ForwardBarrel",&FBarrelBranch, BufferSize);
  outtree->Branch("BackwardBarrel",&BBarrelBranch, BufferSize);
  outtree->Branch("ForwardCD",&FCDBranch, BufferSize);
  outtree->Branch("Miniball",&MiniballBranch, BufferSize);
  outtree->Branch("EbisTime",&EbisTime,"EbisTime/L", BufferSize);
  outtree->Branch("T1Time",&T1Time,"T1Time/L", BufferSize);
  outtree->Branch("SuperCycleTime",&SuperCycleTime,"SuperCycleTime/L", BufferSize);
  outtree->BranchRef();

  Long64_t GByte = 1073741824L;
  outtree->SetMaxTreeSize(2*GByte);

  //if(Settings->VerboseLevel() > 0){
  //  cout<<"attached branch"<<endl;
  //}
  Double_t nentries = intree->GetEntries();
  //check whether last event was set
  //if(0 < Settings->LastEvent() && Settings->LastEvent() < nentries){
  //  nentries = Settings->LastEvent();
  //}

  int status;
  int i;

  int NofAdcChans = sett->NofAdcChans();
  int NofAdcMods = sett->NofAdcs();

  unsigned short AdcVal;
  int AdcChan;
  int ModNum;
  vector<vector<unsigned short> > Value(NofAdcMods, vector<unsigned short>(NofAdcChans));
  vector<long long> Time(NofAdcMods);
  vector<DgfData> Cluster[8];

  //statistics variables
  int TsModsFound;
  
  int gammas =0;
  int strangegammas= 0;
  
  if(VerboseLevel > 0){
    cout << "nr of entries " << nentries << endl;
  }

  for(i=0; i<nentries;i++){
    if(VerboseLevel > 2){
      cout << "@@@@@@@@@@@@@@@@@@@ next event @@@@@@@@@@@@@@@@@" << endl;
      cout << "reading event nr " << i << " of " << nentries << " events " << endl;
    }
    status = intree->GetEvent(i);
    if(status == -1){
      cerr<<"Error occured, couldn't read entry "<<i<<" from tree "<<intree->GetName()<<" in file "<<infile->GetName()<<endl;
      return 5;
    }
    else if(status == 0){
      cerr<<"Error occured, entry "<<i<<" in tree "<<intree->GetName()<<" in file "<<infile->GetName()<<" doesn't exist"<<endl;
      return 6;
    }

    TsModsFound=0;  
    calevent->Clear();
    Time.assign(Time.size(), -1);

    //clear adc values array
    for(int adc=0; adc<Value.size(); adc++){
      Value[adc].assign(Value[adc].size(), 0);
    }
    //clear dgf values
    for(int cluster=0; cluster<8; cluster++){
      Cluster[cluster].clear();
    }
    //order adcs and channels
    for(int adc=0; adc<event->NumberOfAdcs(); adc++){
      Time[event->Adc(adc)->ModuleNumber()] = event->Adc(adc)->Time();
      for(int j=0; j<event->Adc(adc)->SubEvent()->Size(); j++){	      
	if(event->Adc(adc)->SubEvent()->AdcValue(j) <= 3840){ //sliding scale only 0 - 3840 are good!
	  Value[event->Adc(adc)->ModuleNumber()][event->Adc(adc)->SubEvent()->AdcChannel(j)] = event->Adc(adc)->SubEvent()->AdcValue(j);
	  if(VerboseLevel>3){
	    if(Value[event->Adc(adc)->ModuleNumber()][event->Adc(adc)->SubEvent()->AdcChannel(j)] > 300)
	      cout << "Value["<<event->Adc(adc)->ModuleNumber()<<"]["<<event->Adc(adc)->SubEvent()->AdcChannel(j)<<"] = " << Value[event->Adc(adc)->ModuleNumber()][event->Adc(adc)->SubEvent()->AdcChannel(j)] <<endl;
	  }
	}
	else
	  Value[event->Adc(adc)->ModuleNumber()][event->Adc(adc)->SubEvent()->AdcChannel(j)] = 0;
      }
    }
    // calibrate and set barrel
    for(int quadr=0;quadr<4;quadr++){
      if( (Value[sett->RearECDMod(quadr)][sett->FDeltaChan(quadr)]>sett->FDeltaThresh(quadr)) || (Value[sett->RearECDMod(quadr)][sett->FEChan(quadr)]>sett->FEThresh(quadr)) ){ 	
	calevent->SetFBarrel(quadr,Value,Time);
      }
      if(Value[sett->RearECDMod(quadr)][sett->BDeltaChan(quadr)]>sett->BDeltaThresh(quadr)){ 	
	calevent->SetBBarrel(quadr,Value,Time);
      }
    }

    //calibrate and set cd
    // quadr  half  threshs
    // top    0     TB
    // left   0     LR
    // bottom 1     TB
    // right  1     LR
    for(int quadr=0; quadr<4; quadr++){
//      printf("Q = %i, CDL = %i, CDU = %i \n",quadr,sett->CDRingPos(quadr/2,0),sett->CDStripPos(quadr/2,0));
		if( ( Value[sett->RearECDModRing(quadr)][sett->CDRingPos(quadr/2,0)] > sett->CDLowerThreshRing(quadr) && 
	    Value[sett->RearECDModRing(quadr)][sett->CDRingPos(quadr/2,0)] < sett->CDUpperThreshRing(quadr) ) || 
	  ( Value[sett->RearECDModStrip(quadr)][sett->CDStripPos(quadr/2,0)] > sett->CDLowerThreshStrip(quadr) && 
	    Value[sett->RearECDModStrip(quadr)][sett->CDStripPos(quadr/2,0)] < sett->CDUpperThreshStrip(quadr) ) ){
//	printf("Q = %i \n",quadr);
	calevent->SetBCD(quadr, Value, Time, mux);      
      }
    }
    for(int dgf=0; dgf<event->NumberOfDgfs(); dgf++){
      if(event->Dgf(dgf)->ModuleNumber()==sett->BeamdumpDgf()){
	//cout << " BeamDump " << endl;
      }
      else if(event->Dgf(dgf)->ModuleNumber()>=sett->FirstDgf(8)){
	//cout << " TimeStamp " << endl;
      }
      else{
	if(VerboseLevel > 2){
	  cout << " ModuleNumber " << event->Dgf(dgf)->ModuleNumber() << " Channel " << event->Dgf(dgf)->Channel() << " Time " << event->Dgf(dgf)->Time() << " Energy " << event->Dgf(dgf)->Energy() << endl;

	  if(sett->Cluster(event->Dgf(dgf)->ModuleNumber())>-1){
	    cout << "Cluster " << sett->Cluster(event->Dgf(dgf)->ModuleNumber()) << " Crystal " << sett->Crystal(event->Dgf(dgf)->ModuleNumber());
	    if(event->Dgf(dgf)->Channel()==sett->CoreChannel() && event->Dgf(dgf)->ModuleNumber()%2==0)
	      cout << " Core " << endl;
	    else
	      cout << " Segment " << sett->Segment(event->Dgf(dgf)->ModuleNumber(), event->Dgf(dgf)->Channel()) << endl;
	  }
	  else{
	    cout << " ModuleNumber " << event->Dgf(dgf)->ModuleNumber() << endl;
	  }
	}
	ModNum= sett->Cluster(event->Dgf(dgf)->ModuleNumber());
	if(ModNum>=0 && ModNum<8 && event->Dgf(dgf)->Energy()>sett->DgfThresh())
	  Cluster[ModNum].push_back(*(event->Dgf(dgf)));
	if(i==1723 && VerboseLevel > 2){
	  cout << " Cluster " << ModNum << " pushback Cluster" << endl;
	}
	// gamma histos
	if(HistoFile != NULL){
	  if(event->Dgf(dgf)->Channel()==sett->CoreChannel() && event->Dgf(dgf)->ModuleNumber()%2==0)
	    EGamC[sett->Cluster(event->Dgf(dgf)->ModuleNumber())][sett->Crystal(event->Dgf(dgf)->ModuleNumber())]->Fill(event->Dgf(dgf)->Energy());
	  else
	    EGamS[sett->Cluster(event->Dgf(dgf)->ModuleNumber())][sett->Crystal(event->Dgf(dgf)->ModuleNumber())][sett->Segment(event->Dgf(dgf)->ModuleNumber(), event->Dgf(dgf)->Channel())]->Fill(event->Dgf(dgf)->Energy());
	}	
      }
    }//nr of dgfs

    for(int c=0; c<8; c++){
      if(Cluster[c].size() > 0){
	calevent->SetCluster(c, Cluster[c]);
      }
    }
    
    vector<Germanium> *test = calevent->GetCluster();
    for(int g =0; g<(*test).size();g++){
      gammas++;
      if( (*test)[g].strange==1 ){
	strangegammas++;
      }
    }
    /*
    if(i==1723 && VerboseLevel > 2){
      cout << "event " << i << endl;
      vector<Germanium> *test = calevent->GetCluster();
      cout << "size " << (*test).size() << endl;
      for(int g =0; g<(*test).size();g++){
	cout << "nr of crystals " << (*test)[g].NrOfCrystals() << endl;
      }
    }
    */

    // set time
    //calevent->SetTime(event->EbisTime(), event->T1Time(), event->SuperCycleTime()); //important?
    EbisTime = event->EbisTime();
    T1Time = event->T1Time();
    SuperCycleTime = event->SuperCycleTime();
    
    if(calevent->HasData())
      outtree->Fill();
    
    if(i%1000 == 0){
      cout<<setw(5)<<setiosflags(ios::fixed)<<setprecision(1)<<(100.*i)/nentries<<" % done\r"<<flush;
    }
   
  }

  outtree->Write("",TObject::kOverwrite);
  infile->Close();
  outfile->Close();

  if(HistoFile != NULL){
    histofile->cd();
    for(int i=0;i<8;i++){ // cluster
      for(int k=0;k<3;k++){ // crystal
	for(int j=0;j<6;j++){ // segment
	  EGamS[i][k][j]->Write("",TObject::kOverwrite);
	}
	EGamC[i][k]->Write("",TObject::kOverwrite);
      }
    }
  }

  cout<<endl<<"Done!"<<endl;
  cout << "nof gammas " << gammas << endl;
  cout << "strange gammas " << strangegammas << endl;
  return 0;
}
